package java_option.audio_a031.audio;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.io.Serializable;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.Arrays;

import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.DataLine;
import javax.sound.sampled.FloatControl;
import javax.sound.sampled.LineUnavailableException;
import javax.sound.sampled.SourceDataLine;

/**
 * 
 * @author java红了 | java_option <br>
 *         2020-11-920:27:17
 */
public class AudioPlay {

	public static void main(String[] args) {

		File file = new File("d:/test/收藏/张靓颖-千百度.wav");
		new AudioPlay().play(file.getPath(), 0);
	}

	/**
	 * 0 -100 , 0静音
	 */
	private float volume = 100;

	public String type = null;

	/**
	 * 
	 * @param file
	 * @param microseconds 微秒
	 */
	public void play(String filePath, long microseconds) {
		try {
			RandomAccessFile raf = new RandomAccessFile(filePath, "rw");

//
			System.out.println("--------RIFF--------");
			print(raf, 4, false);
			print(raf, 4, true);
			print(raf, 4, false);

//
			System.out.println("--------FORMAT--------");
			print(raf, 4, false);
			print(raf, 4, true);
			print(raf, 2, true);
			int channels = (int) print(raf, 2, true); //

			int sampleRate = (int) print(raf, 4, true); //
			print(raf, 4, true);
			print(raf, 2, true);
			int sampleSizeInBits = (int) print(raf, 2, true); //

//
			System.out.println("--------DATA--------");
			print(raf, 4, false);
			print(raf, 4, true);

			boolean isBigEndian = true;
			AudioFormat format = new AudioFormat(sampleRate, sampleSizeInBits, channels, true, isBigEndian);

			final SourceDataLine auline;
			DataLine.Info info = new DataLine.Info(SourceDataLine.class, format);

			try {
				auline = (SourceDataLine) AudioSystem.getLine(info);
				auline.open(format);
			} catch (LineUnavailableException e) {
				e.printStackTrace();
				return;
			} catch (Exception e) {
				e.printStackTrace();
				return;
			}
			auline.start();
			try {
//
				int headSize = 12 + 24 + 8;
//
				final int bytePerSec = sampleRate * channels * sampleSizeInBits / 8;
				long skipData = microseconds * bytePerSec / (1000 * 1000);
				int blockAlign = channels * sampleSizeInBits / 8;
//
				if (skipData % blockAlign != 0) {
//
					skipData -= skipData % skipData;
				}
				raf.seek(headSize + skipData);
//
				int bytePerFrame = sampleSizeInBits * channels / 8;
				byte[] data = new byte[bytePerFrame];
//
				System.out.println("准备播放 " + filePath);
				System.out.println(microseconds);

				data = new byte[bytePerFrame * 1024];
				while (raf.read(data) > 0) {
					for (int i = 0; i < data.length; i += 4) {
						byte[] tmp = Arrays.copyOfRange(data, i, i + 4);
						if (isBigEndian) {
//
//
							if (type.equals("方式0")) {
								byte[] little4 = revers(tmp);
								short st = ByteBuffer.wrap(little4, 0, 2).order(ByteOrder.LITTLE_ENDIAN).getShort();
								st = (short) (st * (volume / 100.0));
								byte[] newData = ByteBuffer.allocate(2).order(ByteOrder.LITTLE_ENDIAN).putShort(st)
										.array();
								little4[0] = newData[0];
								little4[1] = newData[1];
								little4[2] = 0;
								little4[3] = 0;
								auline.write(little4, 0, little4.length);
							} else if (type.equals("方式1")) {//
//
								short st = ByteBuffer.wrap(tmp, 0, 2).order(ByteOrder.LITTLE_ENDIAN).getShort();
								st = (short) (st * (volume / 100.0));
								byte[] newData = ByteBuffer.allocate(2).order(ByteOrder.LITTLE_ENDIAN).putShort(st)
										.array();
								tmp[0] = 0; 	//
								tmp[1] = 0;
								tmp[2] = newData[0];//
								tmp[3] = newData[1];

								byte[] little4 = revers(tmp);
//
								auline.write(little4, 0, little4.length);
							} else { 
								byte[] little4 = revers(tmp);
								if (type.equals("方式2")) { //
									little4[0] *= (volume / 100.0);
									little4[1] *= (volume / 100.0);
								}
								if (type.equals("方式3")) {
									FloatControl volumeControl = (FloatControl) auline
											.getControl(FloatControl.Type.MASTER_GAIN);
//
//
									volumeControl.setValue(20.0f * (float) Math.log10(volume / 100.0));
								}
								little4[2] = 0;
								little4[3] = 0;
								auline.write(little4, 0, little4.length);
							}
						} else {
							if (type.equals("方式3")) {
								FloatControl volumeControl = (FloatControl) auline
										.getControl(FloatControl.Type.MASTER_GAIN);
								if (volume != -1) {
									volumeControl.setValue(20.0f * (float) Math.log10(volume / 100.0));
								}
							} else if (type.equals("方式2")) {
								tmp[0] *= (volume / 100.0);
								tmp[1] *= (volume / 100.0);
							} else {
								short st = ByteBuffer.wrap(tmp, 0, 2).order(ByteOrder.LITTLE_ENDIAN).getShort();
								if (volume != -1) {
//
									st = (short) (st * (volume / 100.0));
								}
								byte[] newData = ByteBuffer.allocate(2).order(ByteOrder.LITTLE_ENDIAN).putShort(st)
										.array();
								tmp[0] = newData[0];
								tmp[1] = newData[1];
							}
							tmp[2] = 0;
							tmp[3] = 0;
							auline.write(tmp, 0, tmp.length);
						}
					}
					byte db = calculateAudioLevel(data, 0, data.length);
					System.out.println(db);
				}
//
				auline.close();
				raf.close();
			} catch (IOException e) {
				e.printStackTrace();
			}

		} catch (

		FileNotFoundException e) {
			e.printStackTrace();
		}
	}

	public void setVolume(float volume) {
		this.volume = volume;
	}

	public static byte[] revers(byte[] tmp) {
		byte[] reversed = new byte[tmp.length];
		for (int i = 0; i < tmp.length; i++) {
			reversed[i] = tmp[tmp.length - i - 1];
		}
		return reversed;
	}

	private static Serializable print(RandomAccessFile raf, int byteSize, boolean isLittle) {
		byte[] data = new byte[byteSize];
		try {
			raf.read(data);
			if (isLittle) {
//
				if (byteSize == 4) {
					int value = ByteBuffer.wrap(data).order(ByteOrder.LITTLE_ENDIAN).getInt();
					System.out.println(value);
					return value;
				} else {
					int value = ByteBuffer.wrap(data).order(ByteOrder.LITTLE_ENDIAN).getShort();
					System.out.println(value);
					return value;
				}
			} else {
				String str = new String(data);
				System.out.println(str);
				return str;
			}
		} catch (IOException e) {
			e.printStackTrace();
		}
		return null;
	}

	/**
	 * The maximum audio level.
	 */
	public static final byte MAX_AUDIO_LEVEL = 0;

	/**
	 * The minimum audio level.
	 */
	public static final byte MIN_AUDIO_LEVEL = 127;

	/**
	 * Calculates the audio level of a signal with specific <tt>samples</tt>.
	 *
	 * @param samples the samples of the signal to calculate the audio level of
	 * @param offset  the offset in <tt>samples</tt> in which the samples start
	 * @param length  the length in bytes of the signal in <tt>samples<tt>
	 * starting at <tt>offset</tt>
	 * @return the audio level of the specified signal
	 */
	public static byte calculateAudioLevel(byte[] samples, int offset, int length) {
		double rms = 0; //

		for (; offset < length; offset += 2) {
//

			ByteBuffer bb = ByteBuffer.allocate(2);
			bb.order(ByteOrder.LITTLE_ENDIAN);
			bb.put(samples[offset]);
			bb.put(samples[offset + 1]);
			short shortVal = bb.getShort(0);
			double sample = shortVal;
			sample /= Short.MAX_VALUE;
			rms += sample * sample;
		}

		int sampleCount = length / 2;

		rms = (sampleCount == 0) ? 0 : Math.sqrt(rms / sampleCount);

		double db;

		if (rms > 0) {
			db = 20 * Math.log10(rms);
//
			db = -db;
//
//
			if (db > MIN_AUDIO_LEVEL)
				db = MIN_AUDIO_LEVEL;
			else if (db < MAX_AUDIO_LEVEL)
				db = MAX_AUDIO_LEVEL;
		} else {
			db = MIN_AUDIO_LEVEL;
		}

		return (byte) db;
	}
}
